﻿using CasignDesignGA.Casign;
using CasignDesignGA.Casing;
using CasignDesignGA.Well;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CasignDesingGA.Casing
{
    class CasingDesign: ISelectable
    {
        private static double SIGMA_VALUE = 0.25;

        private static double CORRECTION_FACTOR = 5;


        private int lifeCycle;

        public int LifeCycle
        {
            get { return lifeCycle; }
            set { lifeCycle = value; }
        }
        private double aptitude;

        public double Aptitude
        {
            get { return aptitude; }
            set { aptitude = value; }
        }

        private double price;

        public double Price
        {
            get { return price; }
            set { price = value; }
        }

        private List<CasingUnit> casingCollection;

        public List<CasingUnit> CasingCollection
        {
            get { return casingCollection; }
            set { casingCollection = value; }
        }

        public CasingDesign()
        {
            this.casingCollection = new List<CasingUnit>();
        }

        public void calculatePrice()
        {
            CasingUnit firstCasing = this.CasingCollection[0];
            CasingUnit secondCasing = this.CasingCollection[1];
            this.price = firstCasing.Longitude * firstCasing.Price + secondCasing.Longitude * secondCasing.Price;
        }

        public void calculateAptitude(double designFactor)
        {

            //calculateAptitudeOverDesignVSPrice(designFactor);
            calculateAptitudeOverPrice(designFactor);
            //calculateAptitudeOnlyCollapse(designFactor);
        }

        private void calculateAptitudeOnlyCollapse(double designFactor)
        {
            double calculatedAptitude = 0;
            CasingUnit firstCasing = this.CasingCollection[0];
            CasingUnit secondCasing = this.CasingCollection[1];

            this.calculatePrice();

            double burstFactorFirstCasing = CalculateBurstFactor(firstCasing, designFactor);
            double burstFactorSecondCasing = CalculateBurstFactor(secondCasing, designFactor);
            double collapseFactorFirstCasing = CalculateCollapseFactor(firstCasing, designFactor);
            double collapseFactorSecondCasing = CalculateCollapseFactor(secondCasing, designFactor);

            if (burstFactorFirstCasing == 0 || burstFactorSecondCasing == 0)
            {
                this.aptitude = 0;
            }
            else
            {
                if (collapseFactorFirstCasing == 0 || collapseFactorSecondCasing == 0)
                {
                    this.aptitude = 0;
                }
                else
                {
                    //Here comes the magic
                    collapseFactorFirstCasing = CORRECTION_FACTOR*adjustAptitude(collapseFactorFirstCasing, designFactor);
                    collapseFactorSecondCasing = adjustAptitude(collapseFactorSecondCasing, designFactor);
                    calculatedAptitude = (100000) / this.price;
                    this.aptitude = calculatedAptitude;
                }
            }
        }

        private void calculateAptitudeOverPrice(double designFactor)
        {
            double calculatedAptitude = 0;
            CasingUnit firstCasing = this.CasingCollection[0];
            CasingUnit secondCasing = this.CasingCollection[1];

            this.calculatePrice();

            double burstFactorFirstCasing = CalculateBurstFactor(firstCasing, designFactor);
            double burstFactorSecondCasing = CalculateBurstFactor(secondCasing, designFactor);
            double collapseFactorFirstCasing = CalculateCollapseFactor(firstCasing, designFactor);
            double collapseFactorSecondCasing = CalculateCollapseFactor(secondCasing, designFactor);
            

            calculatedAptitude = burstFactorFirstCasing * burstFactorSecondCasing * collapseFactorFirstCasing * collapseFactorSecondCasing;

            if (calculatedAptitude > 0)
            {
                this.aptitude = 1000000 / this.Price;
            }
            else
            {
                this.aptitude = calculatedAptitude;
            }

            
        }

        private void calculateAptitudeOverDesignVSPrice(double designFactor)
        {
            double calculatedAptitude = 0;
            CasingUnit firstCasing = this.CasingCollection[0];
            CasingUnit secondCasing = this.CasingCollection[1];

            this.calculatePrice();

            double burstFactorFirstCasing = CalculateBurstFactor(firstCasing, designFactor);
            burstFactorFirstCasing = adjustAptitude(burstFactorFirstCasing, designFactor);
            double burstFactorSecondCasing = CalculateBurstFactor(secondCasing, designFactor);
            burstFactorSecondCasing = adjustAptitude(burstFactorSecondCasing, designFactor);
            double collapseFactorFirstCasing = CalculateCollapseFactor(firstCasing, designFactor);
            collapseFactorFirstCasing = adjustAptitude(collapseFactorFirstCasing, designFactor);
            double collapseFactorSecondCasing = CalculateCollapseFactor(secondCasing, designFactor);
            collapseFactorSecondCasing = adjustAptitude(collapseFactorSecondCasing, designFactor);

            calculatedAptitude = burstFactorFirstCasing * burstFactorSecondCasing * collapseFactorFirstCasing * collapseFactorSecondCasing;


            //this.aptitude = adjustAptitude(this.aptitude, designFactor);

            this.aptitude = calculatedAptitude / this.Price;
        }

        private double adjustAptitude(double aptitude, double designFactor)
        {
            double adjustedAptitude = 0;
            
            

            adjustedAptitude = aptitude * gaussianFactor(aptitude, designFactor);

            return adjustedAptitude;
        }

        private double gaussianFactor(double aptitude, double designFactor)
        {
            double calculatedGaussian = 0;

            double mu = designFactor;

            double sigma = CasingDesign.SIGMA_VALUE;

            double firstTerm = 1 / (Math.Sqrt(2 * Math.PI * Math.Pow(sigma, 2)));

            double secondTerm = Math.Exp(-1 * Math.Pow((aptitude-mu),2) / (2*Math.Pow(sigma,2)));

            calculatedGaussian =  CORRECTION_FACTOR * firstTerm * secondTerm;

            return  calculatedGaussian;
        }

        private double CalculateCollapseFactor(CasingUnit aCasing, double designFactor)
        {
            CasingProperty collapseProperty = CasingProperties.Instance.getProperty(CasingConstants.COLLAPSE_PROPERTY);
            return CalculateFactor(collapseProperty, designFactor, aCasing.Collapse);
        }

        private double CalculateBurstFactor(CasingUnit aCasing, double designFactor)
        {

            CasingProperty burstProperty = CasingProperties.Instance.getProperty(CasingConstants.BURST_PROPERTY);
            return CalculateFactor(burstProperty, designFactor, aCasing.Burst);
        }

        private double CalculateFactor( CasingProperty aProperty, double designFactor, double designPropertyValue)
        {
            double minBurstFactor = 1000;
            foreach (double key in aProperty.getKeys())
            {
                double value = aProperty.getKeyValue(key);
                double desingFactorCalculated = designPropertyValue / value;
                if (desingFactorCalculated <= designFactor)
                {
                    return 0;
                }
                else
                {
                    if (desingFactorCalculated <= minBurstFactor)
                    {
                        minBurstFactor = desingFactorCalculated;
                    }
                }
            }
            return minBurstFactor;
        }

        public override string ToString()
        {
            string retString = "";

            retString = "First Casing " + this.casingCollection[0].MetricWeight.ToString() + " " + this.casingCollection[0].SteelGrade + " : From 0 to " + this.casingCollection[0].Longitude.ToString();
            retString += "\nSecond Casing " + this.casingCollection[1].MetricWeight.ToString() + " " + this.casingCollection[1].SteelGrade +": From " + this.casingCollection[0].Longitude.ToString() + " to " + Well.Instance.Depth.ToString();
            retString += "\nAptitude: " + this.aptitude.ToString();
            retString += "\nPrice: " + this.Price;
            return retString;

       }
    }
}
